#include <sstream>

#ifdef SYPX_MEMCHECK
#include "mmgr.h"
#endif

#include "Font.h"

namespace SypX
{
	Font::Font(void)
	{
		fontWidths.resize( 128-32 );//hold widths of all characters in our font texture
	}

	Font::~Font(void)
	{
		//release our resources
		//mi->useMesh(0); not needed bcos Font itself is the mesh
		//mi->useTexture(0);

		delete mi;
		mi = 0;		//Must set to 0 to indicate to Sprite's destructor that we have handled the dummy MeshInstance already

	}

	float Font::getFontSizeY()
	{
		return fontSizeY;
	}

	float Font::calcTextWidth(const String& text)
	{
		float total = 0, max = 0;
		for(UInt j = 0; j < text.size(); ++j)
		{
			if (text.at(j) != '\n')
			{
				char c = text.at(j) - 32;	//fontwidths starts from ' ' which is 32 in ascii. Yahhh.... what about '\n'???
				total += fontWidths[c];
			}
			else
			{
				if (max < total)
					max = total;
				total = 0;
			}
		}

		if (max > total)
			total = max;

		return total;
	}

	UInt Font::numOfChars(const String& text,float width)
	{
		assert(width>16.0f && "ARGHHHHHH, WTF are you doing?? width must be larger than 16.0f");
		
		size_t numOfChars = text.size();	//total number of characters
		UInt num = 0;
		float fWidth = 0.0f;

		for(UInt i = 0; i < numOfChars; ++i)
		{
			/* implementation flawed, fails to take into account newlines
			fWidth += fontWidths[ text.at(i) - 32 ];
			if(fWidth > width)
			{                       
				break;
			}
			else
			{
				++num;
			}*/

			if (text.at(i) != '\n')
			{
				fWidth += fontWidths[text.at(i) - 32];
				if (fWidth > width)
					break;
			} else
				fWidth = 0;

			num++;
		}

		return num;
	}


	String Font::wordWrap(const String& text, float width)
	{
		assert(width>16.0f && "ARGHHHHHH, WTF are you doing?? width must be larger than 16.0f");
		size_t size = text.size();	//length of text
		UInt currChar = 0;
		UInt numOfChar;
		UInt nls = 0;
		String t = text;			//Backup text
		while (currChar < size - 1)
		{
			if(calcTextWidth(text.substr(currChar,size - currChar )) < width )
			{//Check if remaining characters can fit into current line
				break;
			}
			//Number of characters for this line, passes in a substring
			//starting from currChar == "Unprocessed string", number of Remaining chars
			numOfChar = numOfChars(text.substr(currChar,size - currChar + 1 ),width);

			//We don't want half broken words, so ommit last word on line
			while(text.at(currChar + numOfChar - 1) != ' ' )				
			{//while it is not a space, keep moving back

				--numOfChar;
				if(numOfChar <= 1)
				{
					numOfChar =     numOfChars(text.substr(currChar,size - currChar),width);
					break;
				}
			}
			//nls (Newlines have to be taken into account)
			t.insert(currChar + numOfChar + nls,"\n");		//insert newline now
			++nls;          
			currChar += numOfChar;//We have processed numOfChars in this loop

		}
		return t;

	}

	void Font::drawText(const SypX::String &text, float x, float y, float z)
	{
		size_t length = text.length();
		size_t oldSize = data.size();
		UInt bufferNeeded;
		if(hasColor)
		{
			bufferNeeded = data.size() + 9 * 6 * length;	//each character has 6 vertices, each vertex has 9 floats(36 bytes)
		}
		else
		{
			bufferNeeded = data.size() + 5 * 6 * length;	//each character has 6 vertices, each vertex has 9 floats(36 bytes)
		}
		data.resize(bufferNeeded);							//allocate the memory required

		char ch;			//The character being rendered
		float currX, currY;	//character positions
		float cx, cy;		//character UVs 

		const float cellW = 1.0f / 16.0f;	//cell widths and heights in UV coordinates
		const float cellH = 1.0f / 8.0f;	

		currX = x;
		currY = y;

		Vec4 color(1.0f,1.0f,1.0f,1.0f);//Default color is white		
		FloatArray::iterator i = data.begin() + oldSize;

		for(UInt j = 0; j < text.size(); ++j)
		{
			ch = text.at(j);

			switch(ch)
			{
			case '/':	//Escape special command
				++j;
				ch = text.at(j);	
				switch(ch)
				{
				case 'c':
					std::stringstream ss(text, std::stringstream::in );
					ss.seekg(j+2, std::ios::beg);                           
					ss>>color.r;
					ss>>color.g;
					ss>>color.b;
					ss>>color.a;
					j = ss.tellg();	
					continue;				
				}
				break;
			case '\n':
				currX = x;
				currY -= fontSizeY; 
				continue;
			}

			ch = ch - 32;//We start rendering from ' ' which is 32 in ascii
			cx = (ch % 16) / 16.0f;	//find column in texture and normalize to 0-1 range
			cy = 0.875f - (ch/16) / 8.0f;	//0.875 start at lower left of character to map

			//top left 
			(*i) = currX;
			++i;
			(*i) = currY + fontSizeY;
			++i;
			(*i) = z;
			++i;			
			(*i) = cx;
			++i;
			(*i) = cy + cellH;
			++i;
			if(hasColor)
			{
				(*i) = color.r;
				++i;
				(*i) = color.g;
				++i;
				(*i) = color.b;
				++i;
				(*i) = color.a;
				++i;
			}

			//btm left
			(*i) = currX;
			++i;
			(*i) = currY;
			++i;
			(*i) = z;
			++i;			
			(*i) = cx;
			++i;
			(*i) = cy;
			++i;
			if(hasColor)
			{
				(*i) = color.r;
				++i;
				(*i) = color.g;
				++i;
				(*i) = color.b;
				++i;
				(*i) = color.a;
				++i;
			}


			//btm right
			(*i) = currX + fontSizeX;
			++i;
			(*i) = currY;
			++i;
			(*i) = z;
			++i;			
			(*i) = cx + cellW;
			++i;
			(*i) = cy;
			++i;
			if(hasColor)
			{
				(*i) = color.r;
				++i;
				(*i) = color.g;
				++i;
				(*i) = color.b;
				++i;
				(*i) = color.a;
				++i;
			}

			//top right
			(*i) = currX + fontSizeX;
			++i;
			(*i) = currY + fontSizeY;
			++i;
			(*i) = z;
			++i;			
			(*i) = cx + cellW;
			++i;
			(*i) = cy + cellH;
			++i;
			if(hasColor)
			{
				(*i) = color.r;
				++i;
				(*i) = color.g;
				++i;
				(*i) = color.b;
				++i;
				(*i) = color.a;
				++i;
			}

			//top left 
			(*i) = currX;
			++i;
			(*i) = currY + fontSizeY;
			++i;
			(*i) = z;
			++i;			
			(*i) = cx;
			++i;
			(*i) = cy + cellH;
			++i;
			if(hasColor)
			{
				(*i) = color.r;
				++i;
				(*i) = color.g;
				++i;
				(*i) = color.b;
				++i;
				(*i) = color.a;
				++i;
			}

			//btm right
			(*i) = currX + fontSizeX;
			++i;
			(*i) = currY;
			++i;
			(*i) = z;
			++i;			
			(*i) = cx + cellW;
			++i;
			(*i) = cy;
			++i;
			if(hasColor)
			{
				(*i) = color.r;
				++i;
				(*i) = color.g;
				++i;
				(*i) = color.b;
				++i;
				(*i) = color.a;
				++i;
			}

			currX += fontWidths[ch];
		}



	}
}